
#include <cstdint>
#include<iostream>
#include <map>
#include<ws2tcpip.h>
#include<vector>
#include <chrono>
#include <memory>
#include <thread>

#include"util.h"

using namespace std;

// 现在决定使用蛇型命名了，之前的小驼峰后续再改吧

#define H264_FILE_NAME "./data/input.h264"
#define AAC_FILE_NAME "./data/input.aac"

// todo：这个大小是用来分包的，
int chunkSize = 65536; // 设置为 1024*4 就可以看到分包的逻辑了 65536
int windowSize = 2500000;
uint32_t nowStreamId = 1;
struct Player {
    int client_fd_;
    bool is_first_;
};
map<string, vector<Player>> session_player;

int getStreamId() {
    return nowStreamId++;
}

#include<chrono>
#include<ctime>

int64_t getNowMillions() {
    using namespace std::chrono;
    system_clock::time_point time_point_now = system_clock::now();
    system_clock::duration duration = time_point_now.time_since_epoch();
    return duration_cast<microseconds>(duration).count() / 1000;
}

class RtmpConnection {
public:
void doClient(int clientFd) {
    printf("newClient=%d\n", clientFd);
    handleShake(clientFd);
    Buffer input_buffer;
    bool isFirstVideo = true;
    while (true) {
        int64_t init_start = getNowMillions();

        // 每一个while的一定会解析出一个完整的message消息才会去做响应处理
        RtmpHeader rtmp_header;
        vector<uint8_t> payload;
        int ret = parseMessage(clientFd, input_buffer, rtmp_header, payload);
        if (ret < 0) {
            printf("client=%d over\n,", clientFd);
            break;
        }

        // ....
        int64_t init_over = getNowMillions();
        cout << "parseMessage:" << init_over - init_start << endl;

        switch (static_cast<RTMP_MESSAGE_TYPE>(rtmp_header.messageType)) {
            case RTMP_MESSAGE_TYPE::SET_CHUNK_SIZE:
                // TODO: 2025-08-02 12:45:18 简单做先不处理  [by:suhuamo]
                printf("SET_CHUNK_SIZE\n");
                break;
            case RTMP_MESSAGE_TYPE::USER_CONTROL:
                printf("USER_CONTROL\n");
                handleUserControl(clientFd, rtmp_header, payload.data());
                break;
            case RTMP_MESSAGE_TYPE::WINDOW_ACK_SIZE:
                printf("WINDOW_ACK_SIZE\n");
                break;
            case RTMP_MESSAGE_TYPE::SET_PEER_BANDWIDTH:
                printf("SET_PEER_BANDWIDTH\n");
                break;
            case RTMP_MESSAGE_TYPE::AUDIO:
                printf("AUDIO\n");
                // TODO: 2025-08-06 00:53:24 先不考虑音频  [by:suhuamo]
                break;
            case RTMP_MESSAGE_TYPE::VIDEO:
                // printf("VIDEO\n");
                // 做一个区分，如果app是record，那么使用本地的录制视频
                if (app_ == "record") {
                    handleSaveVideoData(payload, rtmp_header.messageLength);
                    // 否则就是正常直播推流
                } else {
                    int64_t init_start = getNowMillions();
                    // cout << "online stream" << endl;
                    // 如果是第一帧，那么解析出 avc 数据
                    if (isFirstVideo) {
                        acv_ = payload;
                        isFirstVideo = false;
                    }
                    cout << session_player[name_].size() << endl;
                    for (auto &pair : session_player[name_]) {
                        if (pair.is_first_) {
                            RtmpHeader rtmpHeader;
                            rtmpHeader.fmt = 0;
                            rtmpHeader.csId = (uint8_t)RTMP_CHUNK_ID::VIDEO; // 视频通道
                            rtmpHeader.timestamp = 0;
                            rtmpHeader.messageType = (uint8_t)RTMP_MESSAGE_TYPE::VIDEO; // 视频数据
                            rtmpHeader.messageStreamId = rtmp_header.messageStreamId;
                            rtmpHeader.messageLength = acv_.size();
                            sendRtmpMessage(pair.client_fd_, rtmpHeader, acv_);
                            pair.is_first_ = false;
                        } else {
                            sendRtmpMessage(pair.client_fd_, rtmp_header, payload);
                        }
                    }
                    int64_t init_over = getNowMillions();
                    cout << "VIDEO:" << init_over - init_start << endl;
                }
                break;
            case RTMP_MESSAGE_TYPE::NOTIFY:
                printf("NOTIFY\n");
                handleNotify(clientFd, rtmp_header, payload.data());
                break;
            case RTMP_MESSAGE_TYPE::COMMAND:
                printf("COMMAND\n");
                handleCommand(clientFd, rtmp_header, payload.data());
                break;
            default:
                printf("RTMP_MESSAGE_TYPE: other type\n");
                break;
        }


        // rtmp_header.print();
    }
}

void handleShake(int clientFd) {
    char buf[2048] = {0};
    char version = 0x03;
    // c0
    int len = recv(clientFd, &version, 1, 0);
    if (len != 1) {
        printf("c0 error");
        return ;
    }
    // c1
    len = recv(clientFd, buf, 1536, 0);
    if (len != 1536) {
        printf("c1 error");
        return ;
    }
    // s0
    len = send(clientFd, &version, 1, 0); // 这里的version不应该用客户端的version，而是服务器自己定义version，让客户端来适配服务器端【但其实差别不大，大家都是同一个版本，即3】
    if (len != 1) {
        printf("s0 error");
        return ;
    }
    // s1
    std::vector<char> s1(1536, 0);
    len = send(clientFd, s1.data(), 1536, 0);
    if (len != 1536) {
        printf("s1 error");
        return ;
    }
    // s2
    len = send(clientFd, buf, 1536, 0);
    if (len != 1536) {
        printf("s2 error");
        return ;
    }
    // c2
    len = recv(clientFd, buf, 1536, 0); // 按照设计来说，这里应该验证c2是否等于s1，但大家好像都不验证
    if (len != 1536) {
        printf("c2 error");
        return ;
    }

}

void handleConnect(int client_fd, int work_id) {
    // 先发送三个命令做流量控制
    sendAcknowledgement(client_fd);
    sendPeerBandWidth(client_fd);
    sendChunkSize(client_fd);
    // 再回复当前这个connect命令
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 0;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::COMMAND);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = static_cast<uint8_t>(RTMP_MESSAGE_TYPE::COMMAND);
    rtmpHeader.messageStreamId = 0;

    AMF0Encoder payload;
    payload.writeString("_result");
    payload.writeNumber(work_id);

    //我也不知道这些字段的含义是什么，待补充
    payload.writeObjectBegin();
    payload.writeObjectName("fmsVer");
    payload.writeString("LNX 9,0,124,2");
    payload.writeObjectName("capabilities");
    payload.writeNumber(31);
    payload.writeObjectEnd();

    payload.writeObjectBegin();
    payload.writeObjectName("level");
    payload.writeString("status");
    payload.writeObjectName("code");
    payload.writeString("NetConnection.Connect.Success");
    payload.writeObjectName("description");
    payload.writeString("Connection succeeded");
    payload.writeObjectName("objectEncoding");
    payload.writeNumber(0);
    payload.writeObjectEnd();

    rtmpHeader.messageLength = payload.getData().size();
    sendRtmpMessage(client_fd, rtmpHeader, payload.getData());
}

void handleCreateStream(int client_fd, int workId, uint32_t streamId) {
    // 回复当前命令
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 1;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::COMMAND);
    rtmpHeader.timestampDelta = 0;
    rtmpHeader.messageType = static_cast<uint8_t>(RTMP_MESSAGE_TYPE::COMMAND);

    AMF0Encoder payload;
    //我也不知道这些字段的含义是什么，待补充
    payload.writeString("_result");
    // 这个数字一定要和createStream传过来的写一样，不然对方不会继续响应  [by:suhuamo]
    payload.writeNumber(workId);
    payload.writeNull();
    payload.writeNumber(streamId);
    cout << "create streamId: " << streamId << endl;

    rtmpHeader.messageLength = payload.getData().size();
    sendRtmpMessage(client_fd, rtmpHeader, payload.getData());
}

void handlePublish(int client_fd, int workId, uint32_t streamId) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 0;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::ON_STATUS);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = static_cast<uint8_t>(RTMP_MESSAGE_TYPE::COMMAND);
    rtmpHeader.messageStreamId = streamId;

    AMF0Encoder payload;
    payload.writeString("onStatus");
    payload.writeNumber(workId);
    payload.writeNull();

    //我也不知道这些字段的含义是什么，待补充
    payload.writeObjectBegin();
    payload.writeObjectName("level");
    payload.writeString("status");
    payload.writeObjectName("code");
    payload.writeString("NetStream.Publish.Start");
    payload.writeObjectName("description");
    payload.writeString("publish start");
    payload.writeObjectEnd();

    rtmpHeader.messageLength = payload.getData().size();
    sendRtmpMessage(client_fd, rtmpHeader, payload.getData());
}

void handleNotify(int client_fd, RtmpHeader& rtmp_header, uint8_t *buf) {
    // TODO: 2025-08-06 00:28:51 等后续提取了RtmpMessage，就不需要rtmp_header和buf这两个参数了，就可以合并成一个参数了  [by:suhuamo]
    AMF0Decoder amf0_decoder(buf, rtmp_header.messageLength);
    string method = amf0_decoder.readString();
    if (method == "@setDataFrame") {
        // TODO: 2025-08-06 00:52:12 简单做先不考虑  [by:suhuamo]
    } else {

    }
}

void handleUserControl(int client_fd, RtmpHeader& rtmp_header, uint8_t *buf) {
    uint8_t type = buf[1];
    if (type == static_cast<uint8_t>(RTMP_USER_CONTROL::SET_BUFFER_LENGTH)) {
        printf("Set Buffer Length\n");
    }
}

void sendVideoData(int clientFd, uint32_t streamId) {

    FILE *fp = fopen(H264_FILE_NAME, "rb");
    if (!fp) {
        printf("fopen file %s error\n", H264_FILE_NAME);
        return;
    }

    // 读取整个文件
    fseek(fp, 0, SEEK_END);
    long fileSize = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    vector<uint8_t> fileData(fileSize);
    fread(fileData.data(), 1, fileSize, fp);
    fclose(fp);

    // 分割NALU单元
    vector<vector<uint8_t>> nalus;
    size_t pos = 0;
    while (pos < fileData.size()) {
        // 查找起始码 (0x00000001 或 0x000001)
        if (fileData[pos] != 0 || fileData[pos+1] != 0) {
            pos++;
            continue;
        }

        size_t startCodeLen = 0;
        if (fileData[pos+2] == 1) {  // 3字节起始码
            startCodeLen = 3;
        } else if (fileData[pos+2] == 0 && fileData[pos+3] == 1) {  // 4字节起始码
            startCodeLen = 4;
        } else {
            pos++;
            continue;
        }

        // 查找下一个起始码位置
        size_t nextPos = pos + startCodeLen;
        while (nextPos < fileData.size()) {
            if (nextPos + 3 < fileData.size() &&
                fileData[nextPos] == 0 &&
                fileData[nextPos+1] == 0 &&
                fileData[nextPos+2] == 1) {
                break;
            }
            if (nextPos + 4 < fileData.size() &&
                fileData[nextPos] == 0 &&
                fileData[nextPos+1] == 0 &&
                fileData[nextPos+2] == 0 &&
                fileData[nextPos+3] == 1) {
                break;
            }
            nextPos++;
        }

        // 提取NALU数据
        if (nextPos < fileData.size()) {
            vector<uint8_t> nalu(fileData.begin() + pos + startCodeLen, fileData.begin() + nextPos);
            nalus.push_back(nalu);
            pos = nextPos;
        } else {
            // 最后一个NALU
            vector<uint8_t> nalu(fileData.begin() + pos + startCodeLen, fileData.end());
            nalus.push_back(nalu);
            break;
        }
    }

    // 检查NALU数量
    if (nalus.size() < 3) {
        printf("H.264文件需要至少包含SPS,PPS和IDR帧\n");
        return;
    }

    // 提取SPS和PPS
    vector<uint8_t> sps = nalus[0];
    vector<uint8_t> pps = nalus[1];

    // 构建AVCDecoderConfigurationRecord
    vector<uint8_t> avcConfig;
    avcConfig.push_back(0x01); // configurationVersion
    avcConfig.push_back(sps[1]); // AVCProfileIndication
    avcConfig.push_back(sps[2]); // profile_compatibility
    avcConfig.push_back(sps[3]); // AVCLevelIndication
    avcConfig.push_back(0xFF); // lengthSizeMinusOne (4字节长度)

    // 添加SPS
    avcConfig.push_back(0xE1); // 0b11100001 (1个SPS)
    uint16_t spsLen = sps.size();
    avcConfig.push_back(static_cast<uint8_t>(spsLen >> 8));
    avcConfig.push_back(static_cast<uint8_t>(spsLen));
    avcConfig.insert(avcConfig.end(), sps.begin(), sps.end());

    // 添加PPS
    avcConfig.push_back(0x01); // PPS个数
    uint16_t ppsLen = pps.size();
    avcConfig.push_back(static_cast<uint8_t>(ppsLen >> 8));
    avcConfig.push_back(static_cast<uint8_t>(ppsLen));
    avcConfig.insert(avcConfig.end(), pps.begin(), pps.end());

    // 发送AVC sequence header
    vector<uint8_t> videoTag;
    videoTag.push_back(0x17); // 关键帧 | AVC
    videoTag.push_back(0x00); // AVC sequence header
    videoTag.push_back(0x00); // Composition time (3字节)
    videoTag.push_back(0x00);
    videoTag.push_back(0x00);
    videoTag.insert(videoTag.end(), avcConfig.begin(), avcConfig.end());

    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 0;
    rtmpHeader.csId = (uint16_t)RTMP_CHUNK_ID::VIDEO; // 视频通道
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = (uint8_t)RTMP_MESSAGE_TYPE::VIDEO; // 视频数据
    rtmpHeader.messageStreamId = streamId;
    rtmpHeader.messageLength = videoTag.size();
    sendRtmpMessage(clientFd, rtmpHeader, videoTag);

    // 发送视频帧
    uint32_t currentTimestamp = 0;
    for (size_t i = 2; i < nalus.size(); i++) {
        const auto& nalu = nalus[i];
        uint8_t naluType = nalu[0] & 0x1F; // 获取NALU类型

        videoTag.clear();
        // TODO: 2025-08-01 23:38:57 中途有新的SPS和PPS需要再次构造一次AVC sequence header发送  [by:suhuamo]
        // 设置视频Tag头
        if (naluType == 5) { // IDR帧
            videoTag.push_back(0x17); // 关键帧 | AVC
        } else {
            videoTag.push_back(0x27); // 非关键帧 | AVC
        }
        videoTag.push_back(0x01); // AVC NALU
        videoTag.push_back(0x00); // Composition time (3字节)
        videoTag.push_back(0x00);
        videoTag.push_back(0x00);

        // 添加NALU长度和内容
        uint32_t naluLength = nalu.size();
        videoTag.push_back(static_cast<uint8_t>(naluLength >> 24));
        videoTag.push_back(static_cast<uint8_t>(naluLength >> 16));
        videoTag.push_back(static_cast<uint8_t>(naluLength >> 8));
        videoTag.push_back(static_cast<uint8_t>(naluLength));
        videoTag.insert(videoTag.end(), nalu.begin(), nalu.end());

        // 设置RTMP头
        if (i == 2) {
            rtmpHeader.fmt = 1; // 后续消息使用fmt=1
            currentTimestamp = 40; // 第一帧时间戳
        } else {
            currentTimestamp += 40; // 每帧增加40ms (模拟25fps)
        }

        rtmpHeader.timestampDelta = 40; // 时间戳增量
        rtmpHeader.messageLength = videoTag.size();

        // 发送视频帧
        sendRtmpMessage(clientFd, rtmpHeader, videoTag);

        // 模拟实时帧率 (25fps = 40ms/帧)
        Sleep(40);
    }
}

/// 响应用户控制请求
/// @param client_fd
/// @param rtmp_header
/// @param buf
void handleCommand(int client_fd, RtmpHeader& rtmp_header, uint8_t *buf) {
    // TODO: 2025-08-06 00:28:51 等后续提取了RtmpMessage，就不需要rtmp_header和buf这两个参数了，就可以合并成一个参数了  [by:suhuamo]
    AMF0Decoder amf0_decoder(buf, rtmp_header.messageLength);
    string method = amf0_decoder.readString();
    /**
     * 这里的格式我懂了，在createStream之后，即有 streamId后，格式变成了
     * string，number，null，string，...
     * 命令名称，事务id，空，流名称，...
     * 至少前四个属性是这样固定下来了
     */
    if (method == "connect") {
        printf("connect\n");
        int workId = amf0_decoder.readNumber();
        auto pairs = amf0_decoder.readObject();
        if (app_ == "") {
            app_ = pairs["app"];
        }
        handleConnect(client_fd, workId);
    } else if (method == "releaseStream") {
        printf("releaseStream\n");
// 如果这个流正在播放，那么释放这个流，由现在这个用户来重新推流
// TODO: 2025-08-02 12:43:32 简单做可以先不考虑  [by:suhuamo]
    } else if (method == "FCPPublish") {
        printf("FCPPublish\n");
// 为了兼容老版本的才有这个命令，可以不用处理
    } else if (method == "createStream") {
        printf("createStream\n");
        uint32_t streamId = getStreamId();
        int workId = amf0_decoder.readNumber();
        handleCreateStream(client_fd, workId, streamId);
    } else if (method == "publish") {
        printf("publish\n");
        int workId = amf0_decoder.readNumber();
        amf0_decoder.skipNull();
        string name = amf0_decoder.readString();
        if (name_ == "") {
            name_ = name;
        }
        handlePublish(client_fd, workId, rtmp_header.messageStreamId);
    } else if (method == "getStreamLength") {
        // 拉流客户端传过来的命令
        printf("getStreamLength\n");
    } else if (method == "play") {
        // 拉流客户端传过来的命令
        printf("play\n");
        // TODO: 2025-08-12 23:19:49 待研究workId的作用  [by:suhuamo]
        uint32_t workId = amf0_decoder.readNumber();
        amf0_decoder.skipNull();
        string name = amf0_decoder.readString();
        if (name_ == "") {
            name_ = name;
        }
        // 做一个区分，如果app是record，那么使用本地的录制视频
        if (app_ == "record") {
            // Stream Is Recorded，表明当前流的录制的一个视频流，而不是直播流【应该可以不发送吧？】
            sendStreamIsRecord(client_fd, rtmp_header.messageStreamId);
            // Stream Begin,标识流数据加载好了，可以发送给客户端进行播放了
            sendStreamBegin(client_fd, rtmp_header.messageStreamId);

            /*
            * onStatus('NetStream.Play.Reset') --非必发，用来通知客户端清空缓冲区的
            * onStatus('NetStream.Play.Start') --必发，表明服务器的流数据准备好了，可以给客户端发送数据了，
            * onStatus('NetStream.Data.Start') --必发，通知客户端下一个请求（@setDataFrame）是音视频的元数据（一般是在Play.Start后面马上发送），故流程一般是【Play.Start->Data.Start->@setDataFrame】
            * onStatus('NetStream.Play.PublishNotify') --如果当前的流是点播的视频，那么不需要发。如果当前的流是另一个客户端推送的流，那么必发【本程序演示的是读取视频文件，故不需要发送这个消息】
            */
            sendPlayStartStatus(client_fd, rtmp_header.messageStreamId, workId);
            sendDataStartStatus(client_fd, rtmp_header.messageStreamId, workId);

            // 接下来就是发送音视频数据了
            sendOnMetaData(client_fd, rtmp_header.messageStreamId);
            sendVideoData(client_fd, rtmp_header.messageStreamId);
            // 否则就是使用推的流
        } else {
            // TODO: 2025-08-13 21:33:00 这里需要判断流不存在的情况  [by:suhuamo]
            // Stream Begin,标识流数据加载好了，可以发送给客户端进行播放了
            sendStreamBegin(client_fd, rtmp_header.messageStreamId);

            /*
            * onStatus('NetStream.Play.Reset') --非必发，用来通知客户端清空缓冲区的
            * onStatus('NetStream.Play.Start') --必发，表明服务器的流数据准备好了，可以给客户端发送数据了，
            * onStatus('NetStream.Data.Start') --必发，通知客户端下一个请求（@setDataFrame）是音视频的元数据（一般是在Play.Start后面马上发送），故流程一般是【Play.Start->Data.Start->@setDataFrame】
            * onStatus('NetStream.Play.PublishNotify') --如果当前的流是点播的视频，那么不需要发。如果当前的流是另一个客户端推送的流，那么必发【本程序演示的是读取视频文件，故不需要发送这个消息】
            */
            sendPlayResetStatus(client_fd, rtmp_header.messageStreamId, workId);
            sendPlayStartStatus(client_fd, rtmp_header.messageStreamId, workId);
            sendDataStartStatus(client_fd, rtmp_header.messageStreamId, workId);
            sendPublishNotifyStatus(client_fd, rtmp_header.messageStreamId, workId);


            sendOnMetaData(client_fd, rtmp_header.messageStreamId);
            // sendVideoData(client_fd, rtmp_header.messageStreamId);

            // 添加到这个session的播放列表中，等接受到数据后由接受地方来转发数据
            session_player[name_].push_back({client_fd, true});
            while (!session_player[name_].empty()) {
                Sleep(1000);
            }
        }
    }
    else {
        printf("handleCommand ohter type\n");
    }
}

void handleSaveVideoData(vector<uint8_t> payload, int payloadLength) {

    // RTMP Video 数据解析
    const uint8_t* videoData = payload.data();
    int videoDataLen = payloadLength;

    if (videoDataLen < 5) return; // 至少要有5字节头

    uint8_t frameType = (videoData[0] >> 4) & 0x0F; // 1=关键帧, 2=非关键帧
    uint8_t codecID   = (videoData[0] & 0x0F);      // 7=AVC(H.264)
    uint8_t avcPacketType = videoData[1];           // 0=配置(SPS/PPS), 1=NALU数据
    // int32_t compositionTime = (videoData[2] << 16) | (videoData[3] << 8) | videoData[4]; // 可能用不上

    if (codecID != 7) {
        printf("非H.264视频数据，忽略\n");
        return ;
    }

    FILE* h264FileFp = fopen("./output.h264", "ab+");
    if (!h264FileFp) return;

    if (avcPacketType == 0) {
        // AVCDecoderConfigurationRecord，提取SPS/PPS
        if (videoDataLen < 6) {
            fclose(h264FileFp);
            return;
        }
        const uint8_t* p = videoData + 5;
        p++; // configurationVersion
        p += 3; // AVCProfileIndication, profile_compatibility, AVCLevelIndication
        p++; // lengthSizeMinusOne
        uint8_t numOfSPS = (*p++) & 0x1F;
        for (int i = 0; i < numOfSPS; i++) {
            uint16_t spsLen = (p[0] << 8) | p[1];
            p += 2;
            fwrite("\x00\x00\x00\x01", 1, 4, h264FileFp);
            fwrite(p, 1, spsLen, h264FileFp);
            p += spsLen;
        }
        uint8_t numOfPPS = *p++;
        for (int i = 0; i < numOfPPS; i++) {
            uint16_t ppsLen = (p[0] << 8) | p[1];
            p += 2;
            fwrite("\x00\x00\x00\x01", 1, 4, h264FileFp);
            fwrite(p, 1, ppsLen, h264FileFp);
            p += ppsLen;
        }
    }
    else if (avcPacketType == 1) {
        // NALU 数据，带长度前缀，需要转成start code
        const uint8_t* p = videoData + 5;
        int remain = videoDataLen - 5;
        while (remain > 4) {
            uint32_t naluLen = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
            p += 4;
            remain -= 4;
            if (naluLen > (uint32_t)remain) break; // 数据异常
            fwrite("\x00\x00\x00\x01", 1, 4, h264FileFp);
            fwrite(p, 1, naluLen, h264FileFp);
            p += naluLen;
            remain -= naluLen;
        }
    }
    else {
        // avcPacketType == 2 未压缩帧，这里直接忽略
    }

    fclose(h264FileFp);

}

int parseMessage(int client_fd, Buffer &input_buffer, RtmpHeader &rtmp_header, vector<uint8_t> &payload) {
    char buf[chunkSize + 1] = {0};
    int len = 0;
    // 先读取一个 header 长度的数据，用来解析 message 的第一个 chunk
    while (input_buffer.Size() < sizeof(RtmpHeader)) {
        Sleep(10);
        len = recv(client_fd, buf, chunkSize, 0);
        input_buffer.Append(buf, len);
        if (len < 0) {
            return -1;
        }
    }
    // TODO: 2025-08-12 22:44:57 其实按道理应该开一个线程一直recv数据，而不是recv一点数据就去解析header了，即使用IO多路复用模型  [by:suhuamo]
    rtmp_header = parseRtmpHeader(reinterpret_cast<const uint8_t *>(input_buffer.Data()));

    input_buffer.Erase(0, rtmp_header.idx);

    // 再读取 messageLength长度的数据
    while (input_buffer.Size() < rtmp_header.messageLength) {
        Sleep(10);
        len = recv(client_fd, buf, chunkSize, 0);
        input_buffer.Append(buf, len);
        if (len < 0) {
            return -1;
        }
    }

    // 将本次读取到的多个chunk(本来就属于同一个message)合并为一个message
    int payloadLength = 0;
    int readByte = 0;
    payload.resize(rtmp_header.messageLength);
    while (payloadLength < rtmp_header.messageLength) {
        int thisChunkSize = rtmp_header.messageLength - readByte;
        // 读取当前chunk的payload
        if (thisChunkSize <= chunkSize) {
            memcpy(payload.data() + payloadLength, input_buffer.Data() + readByte, thisChunkSize);
            payloadLength += thisChunkSize;
            readByte += thisChunkSize;
        } else {
            thisChunkSize = chunkSize;
            memcpy(payload.data() + payloadLength, input_buffer.Data() + readByte, thisChunkSize);
            payloadLength += thisChunkSize;
            readByte += thisChunkSize;
            // 再读取下一个chunk的header
            readByte += parseRtmpHeader(reinterpret_cast<const uint8_t *>(input_buffer.Data()) + readByte).idx;
        }
    }

    input_buffer.Erase(0, readByte);
    // tcp有的时候末尾会多传一个\t，好奇怪，我看其他人的项目代码都没这么写
    if (input_buffer.Data()[0] == '\t') {
        input_buffer.Erase(0, 1);
    }
    return 0;
}

/// 发送rtmp消息
/// @param clientFd 套接字
/// @param rtmpHeader 请求头
/// @param payload 载荷
void sendRtmpMessage(int clientFd, const RtmpHeader& rtmpHeader, const vector<uint8_t>& payload) {
    // 1. 构造基本头部 (Basic Header)
    vector<uint8_t> basicHeader;
    if (rtmpHeader.csId <= 63) { // 0~63
        basicHeader.push_back((rtmpHeader.fmt << 6) | rtmpHeader.csId);
    } else if (rtmpHeader.csId <= 319) { // 255 + 64
        basicHeader.push_back(rtmpHeader.fmt << 6);
        basicHeader.push_back(rtmpHeader.csId - 64);
    } else {
        basicHeader.push_back((rtmpHeader.fmt << 6) | 0x1F);
        uint16_t adjustedCsId = rtmpHeader.csId - 64;
        basicHeader.push_back(adjustedCsId >> 8);
        basicHeader.push_back(adjustedCsId & 0xFF);
    }

    // 2. 构造消息头部 (Message Header)
    vector<uint8_t> messageHeader;
    if (rtmpHeader.fmt == 0) {
        // fmt=0: 11字节头
        messageHeader.push_back(rtmpHeader.timestamp >> 16);
        messageHeader.push_back((rtmpHeader.timestamp >> 8) & 0xFF);
        messageHeader.push_back(rtmpHeader.timestamp & 0xFF);

        messageHeader.push_back(rtmpHeader.messageLength >> 16);
        messageHeader.push_back((rtmpHeader.messageLength >> 8) & 0xFF);
        messageHeader.push_back(rtmpHeader.messageLength & 0xFF);

        messageHeader.push_back(rtmpHeader.messageType);

        // TODO: 2025-08-10 22:57:06 好奇怪，为什么 body size 等都是网络序（大端），只有messageStreamId是主机序  [by:suhuamo]
        messageHeader.push_back(rtmpHeader.messageStreamId & 0xFF);
        messageHeader.push_back((rtmpHeader.messageStreamId >> 8) & 0xFF);
        messageHeader.push_back((rtmpHeader.messageStreamId >> 16) & 0xFF);
        messageHeader.push_back(rtmpHeader.messageStreamId >> 24);

    } else if (rtmpHeader.fmt == 1) {
        // fmt=1: 7字节头
        messageHeader.push_back(rtmpHeader.timestampDelta >> 16);
        messageHeader.push_back((rtmpHeader.timestampDelta >> 8) & 0xFF);
        messageHeader.push_back(rtmpHeader.timestampDelta & 0xFF);

        messageHeader.push_back(rtmpHeader.messageLength >> 16);
        messageHeader.push_back((rtmpHeader.messageLength >> 8) & 0xFF);
        messageHeader.push_back(rtmpHeader.messageLength & 0xFF);

        messageHeader.push_back(rtmpHeader.messageType);
    } else if (rtmpHeader.fmt == 2) {
        // fmt=2: 3字节头
        messageHeader.push_back(rtmpHeader.timestampDelta >> 16);
        messageHeader.push_back((rtmpHeader.timestampDelta >> 8) & 0xFF);
        messageHeader.push_back(rtmpHeader.timestampDelta & 0xFF);
    }
    // fmt=3 没有消息头

    // 4. 如果消息体小于chunkSize，直接发送完整消息
    if (payload.size() <= chunkSize) {
        vector<uint8_t> fullPacket;
        fullPacket.insert(fullPacket.end(), basicHeader.begin(), basicHeader.end());
        fullPacket.insert(fullPacket.end(), messageHeader.begin(), messageHeader.end());
        fullPacket.insert(fullPacket.end(), payload.begin(), payload.end());

        int len = send(clientFd, reinterpret_cast<const char*>(fullPacket.data()), fullPacket.size(), 0);
        if (len != static_cast<int>(fullPacket.size())) {
            printf("send size %d != data size %zu\n", len, fullPacket.size());
        }
        return;
    }

    // 5. 分块发送长消息
    size_t bytesSent = 0;
    bool firstChunk = true;

    while (bytesSent < payload.size()) {
        vector<uint8_t> chunk;

        if (firstChunk) {
            // 第一块包含完整头部
            chunk.insert(chunk.end(), basicHeader.begin(), basicHeader.end());
            chunk.insert(chunk.end(), messageHeader.begin(), messageHeader.end());
            firstChunk = false;
        } else {
            // 后续块使用fmt=3头部 (只有基本头)
            uint8_t fmt3Header = (3 << 6); // fmt=3
            if (rtmpHeader.csId <= 63) {
                fmt3Header |= rtmpHeader.csId;
                chunk.push_back(fmt3Header);
            } else if (rtmpHeader.csId <= 319) {
                chunk.push_back(fmt3Header);
                chunk.push_back(rtmpHeader.csId - 64);
            } else {
                chunk.push_back(fmt3Header | 0x1F);
                uint16_t adjustedCsId = rtmpHeader.csId - 64;
                chunk.push_back(adjustedCsId >> 8);
                chunk.push_back(adjustedCsId & 0xFF);
            }
            cout << "chunk ..." << endl;
        }

        // 计算本块的数据大小
        size_t dataSize = min((unsigned long long)chunkSize, payload.size() - bytesSent);

        // 添加数据到块
        chunk.insert(chunk.end(), payload.begin() + bytesSent, payload.begin() + bytesSent + dataSize);
        bytesSent += dataSize;

        // 发送块
        int len = send(clientFd, reinterpret_cast<const char*>(chunk.data()), chunk.size(), 0);
        if (len != static_cast<int>(chunk.size())) {
            printf("send chunk size %d != %zu\n", len, chunk.size());
        }
    }
}

void sendAcknowledgement(int clientFd) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 0;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::USER_CONTROL);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = static_cast<uint8_t>(RTMP_MESSAGE_TYPE::WINDOW_ACK_SIZE);
    rtmpHeader.messageStreamId = 0;

    vector<uint8_t> payload;
    payload.push_back(windowSize >> 24);
    payload.push_back((windowSize >> 16) & 0xFF);
    payload.push_back((windowSize >> 8) & 0xFF);
    payload.push_back(windowSize & 0xFF);

    rtmpHeader.messageLength = payload.size();
    sendRtmpMessage(clientFd, rtmpHeader, payload);
}

void sendPeerBandWidth(int clientFd) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 1;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::USER_CONTROL);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = static_cast<uint8_t>(RTMP_MESSAGE_TYPE::SET_PEER_BANDWIDTH);

    vector<uint8_t> payload;
    payload.push_back(windowSize >> 24);
    payload.push_back((windowSize >> 16) & 0xFF);
    payload.push_back((windowSize >> 8) & 0xFF);
    payload.push_back(windowSize & 0xFF);
    payload.push_back(0x02); // 约束类型【待补充文档说明】

    rtmpHeader.messageLength = payload.size();
    sendRtmpMessage(clientFd, rtmpHeader, payload);
}

void sendChunkSize(int clientFd) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 1;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::USER_CONTROL);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = static_cast<uint8_t>(RTMP_MESSAGE_TYPE::SET_CHUNK_SIZE);

    vector<uint8_t> payload;
    payload.push_back(chunkSize >> 24);
    payload.push_back((chunkSize >> 16) & 0xFF);
    payload.push_back((chunkSize >> 8) & 0xFF);
    payload.push_back(chunkSize & 0xFF);

    rtmpHeader.messageLength = payload.size();
    sendRtmpMessage(clientFd, rtmpHeader, payload);
}

void sendStreamIsRecord(int clientFd, uint32_t streamId) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 1;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::USER_CONTROL);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = (uint8_t)(RTMP_MESSAGE_TYPE::USER_CONTROL);

    vector<uint8_t> payload;
    payload.push_back(0);
    payload.push_back(static_cast<uint16_t>(RTMP_USER_CONTROL::STREAM_IS_RECORDED)); // 4 代表 StreamIsRecord

    payload.push_back(streamId >> 24);
    payload.push_back((streamId >> 16) & 0xFF);
    payload.push_back((streamId >> 8) & 0xFF);
    payload.push_back(streamId & 0xFF);

    rtmpHeader.messageLength = payload.size();
    sendRtmpMessage(clientFd, rtmpHeader, payload);
}

void sendStreamBegin(int clientFd, uint32_t streamId) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 0;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::USER_CONTROL);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = (uint8_t)(RTMP_MESSAGE_TYPE::COMMAND);
    rtmpHeader.messageStreamId = streamId;

    vector<uint8_t> payload;
    payload.push_back(0);
    payload.push_back(static_cast<uint16_t>(RTMP_USER_CONTROL::STREAM_BEGIN)); // 0 代表StreamBegin

    payload.push_back(streamId >> 24);
    payload.push_back((streamId >> 16) & 0xFF);
    payload.push_back((streamId >> 8) & 0xFF);
    payload.push_back(streamId & 0xFF);

    rtmpHeader.messageLength = payload.size();
    sendRtmpMessage(clientFd, rtmpHeader, payload);
}

void sendPlayResetStatus(int clientFd, int streamId, uint32_t workId) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 0;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::ON_STATUS);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = (uint8_t)(RTMP_MESSAGE_TYPE::COMMAND);
    rtmpHeader.messageStreamId = streamId;

    AMF0Encoder payload;
    payload.writeString("onStatus");
    payload.writeNumber(workId);
    payload.writeNull();

    //我也不知道这些字段的含义是什么，待补充
    payload.writeObjectBegin();
    payload.writeObjectString("clientid", "0");
    payload.writeObjectString("code", "NetStream.Play.Reset");
    payload.writeObjectString("description", "Resetting and Playing");
    payload.writeObjectString("details", name_);
    payload.writeObjectString("level", "status");
    payload.writeObjectEnd();

    rtmpHeader.messageLength = payload.getData().size();
    sendRtmpMessage(clientFd, rtmpHeader, payload.getData());
}

void sendPlayStartStatus(int clientFd, int streamId, uint32_t workId) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 0;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::ON_STATUS);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = (uint8_t)(RTMP_MESSAGE_TYPE::COMMAND);
    rtmpHeader.messageStreamId = streamId;

    AMF0Encoder payload;
    payload.writeString("onStatus");
    payload.writeNumber(workId);
    payload.writeNull();

    //我也不知道这些字段的含义是什么，待补充
    payload.writeObjectBegin();
    payload.writeObjectName("level");
    payload.writeString("status");
    payload.writeObjectName("code");
    payload.writeString("NetStream.Play.Start");
    payload.writeObjectName("description");
    payload.writeString("play start");
    payload.writeObjectEnd();

    rtmpHeader.messageLength = payload.getData().size();
    sendRtmpMessage(clientFd, rtmpHeader, payload.getData());
}

void sendDataStartStatus(int clientFd, int streamId, uint32_t workId) {
    RtmpHeader rtmpHeader;
    // 由于跟着 sendPlayStartStatus 发送的，故fmt可以设置为3
    rtmpHeader.fmt = 0;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::ON_STATUS);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = (uint8_t)(RTMP_MESSAGE_TYPE::COMMAND);
    rtmpHeader.messageStreamId = streamId;

    AMF0Encoder payload;
    payload.writeString("onStatus");
    payload.writeNumber(workId);
    payload.writeNull();

    //我也不知道这些字段的含义是什么，待补充
    payload.writeObjectBegin();
    payload.writeObjectName("level");
    payload.writeString("status");
    payload.writeObjectName("code");
    payload.writeString("NetStream.Data.Start");
    payload.writeObjectName("description");
    payload.writeString("data start");
    payload.writeObjectEnd();

    rtmpHeader.messageLength = payload.getData().size();
    sendRtmpMessage(clientFd, rtmpHeader, payload.getData());
}

void sendPublishNotifyStatus(int clientFd, int streamId, uint32_t workId) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 0;
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::ON_STATUS);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = (uint8_t)(RTMP_MESSAGE_TYPE::COMMAND);
    rtmpHeader.messageStreamId = streamId;

    AMF0Encoder payload;
    payload.writeString("onStatus");
    payload.writeNumber(workId);
    payload.writeNull();

    //我也不知道这些字段的含义是什么，待补充
    payload.writeObjectBegin();
    payload.writeObjectString("clientid", "0");
    payload.writeObjectString("code", "NetStream.Play.PublishNotify");
    payload.writeObjectString("description", "Now published");
    payload.writeObjectString("details", name_);
    payload.writeObjectString("level", "status");
    payload.writeObjectEnd();

    rtmpHeader.messageLength = payload.getData().size();
    sendRtmpMessage(clientFd, rtmpHeader, payload.getData());
}

void sendOnMetaData(int clientFd, int streamId) {
    RtmpHeader rtmpHeader;
    rtmpHeader.fmt = 0;
    // TODO: 2025-08-12 23:24:52 奇怪，为什么这个是audio类型？  [by:suhuamo]
    rtmpHeader.csId = static_cast<uint16_t>(RTMP_CHUNK_ID::AUDIO);
    rtmpHeader.timestamp = 0;
    rtmpHeader.messageType = (uint8_t)(RTMP_MESSAGE_TYPE::NOTIFY);; //18代表数据相关的消息，20代表命令相关的消息
    rtmpHeader.messageStreamId = streamId;

    AMF0Encoder payload;
    payload.writeString("@setDataFrame");
    payload.writeString("onMetaData");

    //我也不知道这些字段的含义是什么，待补充
    payload.writeObjectBegin();
    payload.writeObjectName("videodatarate");
    payload.writeNumber(0);
    payload.writeObjectName("videocodecid");
    payload.writeNumber(7);
    payload.writeObjectName("audiodatarate");
    payload.writeNumber(0);
    payload.writeObjectName("audiocodecid");
    payload.writeNumber(2);
    payload.writeObjectEnd();

    rtmpHeader.messageLength = payload.getData().size();
    sendRtmpMessage(clientFd, rtmpHeader, payload.getData());
}

RtmpHeader parseRtmpHeader(const uint8_t buf[]) {
    RtmpHeader rtmp_header;
    // 解析Header
    rtmp_header.fmt = buf[rtmp_header.idx] >> 6;
    rtmp_header.csId = buf[rtmp_header.idx++] & 0b00111111;
    if (rtmp_header.csId == 0x00) {
        // 此时是 csId 为一个字节的情况
        rtmp_header.csId = buf[rtmp_header.idx++] + 64;
    } else if (rtmp_header.csId == 0b00111111) {
        // 此时是 csId 为两个字节的情况
        rtmp_header.csId = (((unsigned char)(buf[rtmp_header.idx++])) << 8) |
                ((unsigned char)(buf[rtmp_header.idx++])) + 64;
    } else {
        // 此时是csId为高五位的情况
    }
    rtmp_header.timestamp = 0;
    rtmp_header.timestampDelta = 0;
    rtmp_header.messageLength = 0;
    rtmp_header.messageType = 0;
    rtmp_header.messageStreamId = 0;


    if (rtmp_header.fmt == 0) {
        rtmp_header.timestamp = (((unsigned char)(buf[rtmp_header.idx++])) << 16) |
                (((unsigned char)(buf[rtmp_header.idx++])) << 8) |
                 ((unsigned char)(buf[rtmp_header.idx++]));
        rtmp_header.messageLength = (((unsigned char)(buf[rtmp_header.idx++])) << 16) |
                    (((unsigned char)(buf[rtmp_header.idx++])) << 8) |
                     ((unsigned char)(buf[rtmp_header.idx++]));
        rtmp_header.messageType = ((unsigned char)(buf[rtmp_header.idx++]));
        // 这里为什么解析要使用主机序解析  [by:suhuamo]
        rtmp_header.messageStreamId = (((unsigned char)(buf[rtmp_header.idx++]))) |
                              (((unsigned char)(buf[rtmp_header.idx++])) << 8) |
                              (((unsigned char)(buf[rtmp_header.idx++])) << 16) |
                               ((unsigned char)(buf[rtmp_header.idx++]) << 24);
    }
    else if (rtmp_header.fmt == 1) {
        rtmp_header.timestampDelta = (((unsigned char)(buf[rtmp_header.idx++])) << 16) |
                       (((unsigned char)(buf[rtmp_header.idx++])) << 8) |
                        ((unsigned char)(buf[rtmp_header.idx++]));
        rtmp_header.messageLength = (((unsigned char)(buf[rtmp_header.idx++])) << 16) |
                    (((unsigned char)(buf[rtmp_header.idx++])) << 8) |
                     ((unsigned char)(buf[rtmp_header.idx++]));
        rtmp_header.messageType = ((unsigned char)(buf[rtmp_header.idx++]));
    } else if (rtmp_header.fmt == 2) {
        rtmp_header.timestampDelta = (((unsigned char)(buf[rtmp_header.idx++])) << 16) |
               (((unsigned char)(buf[rtmp_header.idx++])) << 8) |
                ((unsigned char)(buf[rtmp_header.idx++]));
    } else {
        // 即fmt=3的情况，没有Message Header
    };


    // 解析header结束了，接下来判断是否需要获取之前的历史数据
    if (rtmp_header.fmt == 0) {
        rtmp_header_map[rtmp_header.csId] = rtmp_header;
        // 根据上一个csId的参数，补充本次chunk的信息
    } else if (rtmp_header.fmt == 1){
        rtmp_header.messageStreamId = rtmp_header_map[rtmp_header.csId].messageStreamId;
        rtmp_header.timestamp = rtmp_header_map[rtmp_header.csId].timestamp + rtmp_header.timestampDelta;

        rtmp_header_map[rtmp_header.csId].timestamp = rtmp_header.timestamp;
        rtmp_header_map[rtmp_header.csId].timestampDelta = rtmp_header.timestampDelta;
        rtmp_header_map[rtmp_header.csId].messageLength = rtmp_header.messageLength;
    } else if (rtmp_header.fmt == 2){
        rtmp_header.messageLength = rtmp_header_map[rtmp_header.csId].messageLength;
        rtmp_header.messageType = rtmp_header_map[rtmp_header.csId].messageType;
        rtmp_header.messageStreamId = rtmp_header_map[rtmp_header.csId].messageStreamId;
        rtmp_header.timestamp = rtmp_header_map[rtmp_header.csId].timestamp + rtmp_header.timestampDelta;

        rtmp_header_map[rtmp_header.csId].timestamp = rtmp_header.timestamp;
        rtmp_header_map[rtmp_header.csId].timestampDelta = rtmp_header.timestampDelta;
    } else {
        rtmp_header.messageLength = rtmp_header_map[rtmp_header.csId].messageLength;
        rtmp_header.messageType = rtmp_header_map[rtmp_header.csId].messageType;
        rtmp_header.messageStreamId = rtmp_header_map[rtmp_header.csId].messageStreamId;
        rtmp_header.timestamp = rtmp_header_map[rtmp_header.csId].timestamp;
    }
    return rtmp_header;
}

void printRecv(const int len, const char *buf) {
    // 1. 打印原始长度
    printf("len=%d\n", len);

    // 2. 以十六进制格式输出所有字节
    printf("Hex dump: ");
    for (int i = 0; i < len; ++i) {
        printf("%02X ", static_cast<unsigned char>(buf[i]));
    }
    printf("\n");

    // 3. 新增：以二进制格式输出所有字节
    printf("Binary dump: ");
    for (int i = 0; i < len; ++i) {
        unsigned char byte = static_cast<unsigned char>(buf[i]);
        for (int j = 7; j >= 0; j--) {
            putchar((byte & (1 << j)) ? '1' : '0');
        }
        putchar(' ');  // 字节间用空格分隔
    }
    printf("\n");

    // 4. 按字符形式输出
    printf("Text dump: ");
    for (int i = 0; i < len; ++i) {
        if (isprint(static_cast<unsigned char>(buf[i]))) {
            putchar(buf[i]);
        } else {
            putchar('.');
        }
    }
    printf("\n");
}

    map<int, RtmpHeader> rtmp_header_map;
    string app_;
    string name_;
    vector<uint8_t> acv_;
};

/*
 *
 * 推流命令：
 *    保存视频：  ffmpeg -re -i test.mp4 -c:v libx264 -f flv rtmp://127.0.0.1:1935/record/test1
 *    推流直播：  ffmpeg -re -i test.mp4 -c:v libx264 -f flv rtmp://127.0.0.1:1935/live/test2
 * 拉流命令：
 *    点播视频：  ffplay rtmp://127.0.0.1:1935/record/test1
 *    拉流直播：  ffplay rtmp://127.0.0.1:1935/live/test2
 */
int main() {
    system("chcp 65001");
    int port = 1935;
    printf("rtmpServer rtmp://127.0.0.1:%d\n", port);;

    printf(R"( * 推流命令：
 *    保存视频：  ffmpeg -re -i test.mp4 -c:v libx264 -f flv rtmp://127.0.0.1:1935/record/test1
 *    推流直播：  ffmpeg -re -i test.mp4 -c:v libx264 -f flv rtmp://127.0.0.1:1935/live/test2
 * 拉流命令：
 *    点播视频：  ffplay rtmp://127.0.0.1:1935/record/test1
 *    拉流直播：  ffplay rtmp://127.0.0.1:1935/live/test2)");
    printf("\n");

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup error");
        return -1;
    }

    int serverFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverFd == SOCKET_ERROR) {
        printf("socket error");
        return -1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(port);
    if (bind(serverFd, (sockaddr*)&serverAddr, sizeof(sockaddr)) != 0) {
        printf("bind error");
        return -1;
    }

    if (listen(serverFd, SOMAXCONN) < 0) {
        printf("listen error");
        return -1;
    }

    while (true) {
        printf("wait new connection....\n");
        sockaddr_in clientAddr;
        int clientAddrLen = sizeof(sockaddr_in);
        int clientFd = accept(serverFd, (sockaddr*)&clientAddr, &clientAddrLen);
        if (clientFd == SOCKET_ERROR) {
            printf("accept error\n");
            break;
        }
        std::thread([clientFd]() -> void{
            RtmpConnection rtmp_connection;
            rtmp_connection.doClient(clientFd);
        }).detach();
        printf("\n");
    }

    closesocket(serverFd);
    printf("RtmpServer over\n");
    return 0;
}